package com.yhq.spring.manual;

import lombok.SneakyThrows;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ClassUtils;

import java.beans.Introspector;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author hqyin
 * @date 2023/4/7 10:14 上午
 */
public class MyBeanFactory {
    final Map<String,CustomBeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
    /**
     * 一级缓存，存放完整bean
     */
    private final Map<String,Object> singletonObjects = new ConcurrentHashMap<>(256);
    /**
     * 存放创建完成未初始化未属性填充的对象
     */
    private final  Map<String,Object> earlySingletonObjects = new ConcurrentHashMap(16);
    /**
     *
     */
    private final Map<String,ObjectFunction<?>> singletonFactories = new HashMap<>(16);

    private final Set<String> singletonCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));



    public Object getBean(String beanName) {
        Object instance = getSingleton(beanName,true);
        if(instance != null) {
            return instance;
        }

        //创建单例对象
        CustomBeanDefinition bd = this.beanDefinitionMap.get(beanName);
        if(bd.isSingleton() ) {
          return getSingleton(beanName,()-> createBean(beanName,bd));
        }

        //原型
        if(!bd.isSingleton()) {
            return createBean(beanName,bd);
        }

        return null;
    }

    private <T> Object getSingleton(String beanName, ObjectFunction<T> function) {
        //标记对象正在创建
        this.singletonCurrentlyInCreation.add(beanName);

        //创建对象
        Object instance = function.getObject();
        //移除创建中标记
        this.singletonCurrentlyInCreation.remove(beanName);
        //并缓存到一级缓存中
        this.singletonObjects.put(beanName,instance);
        //删除二级缓存
        this.earlySingletonObjects.remove(beanName);

        return instance;
    }

    @SneakyThrows
    private Object createBean(String beanName, CustomBeanDefinition bd) {
        Class<?> clazz = Class.forName(bd.getType());
        bd.setBeanClass(clazz);

        return doCreateBean(beanName,bd);
    }

    @SneakyThrows
    private Object doCreateBean(String beanName, CustomBeanDefinition bd) {
        //创建对象
        Object instance = bd.getBeanClass().newInstance();
        if(bd.SCOPE_SINGLETON.equals(bd.getScope())) {
            this.earlySingletonObjects.put(beanName,instance);
        }

        //属性填充
        populateBean(instance);



        //初始化
        return instance;
    }

    @SneakyThrows
    private void populateBean(Object instance) {
        Field[] fields =  instance.getClass().getDeclaredFields();
        for (Field field : fields) {
            if(field.isAnnotationPresent(Autowired.class)) {
                String fieldName = field.getName();
                Object value =  getBean(fieldName);
                field.setAccessible(true);
                field.set(instance,value);
            }
        }
    }

    private Object getSingleton(String beanName, boolean allowEarlyReference) {
        Object instance =  this.singletonObjects.get(beanName);
        if(instance == null && this.singletonCurrentlyInCreation.contains(beanName)) {
            instance = this.earlySingletonObjects.get(beanName);
        }
        return instance;
    }

}
